#pragma once
#include "../mqcommon/helper.hpp"
#include "../mqcommon/logger.hpp"
#include "../mqcommon/msg.pb.h"
#include <unordered_map>
#include <mutex>
struct Binding
{
    using Ptr = std::shared_ptr<Binding>;
    std::string exchangeName;
    std::string msgQueueName;
    std::string bindingKey;
    Binding(){}
    Binding(const std::string& eName, const std::string& msgName, const std::string& key)
        :exchangeName(eName), msgQueueName(msgName), bindingKey(key)
    {
    }
    ~Binding(){}
};

using MsgQueueBindingMap = std::unordered_map<std::string, Binding::Ptr>;
using ExchangeBindingMap = std::unordered_map<std::string, MsgQueueBindingMap>;
class BindingMapper
{
public:
    BindingMapper(const std::string& dbfile)
        :_helper(dbfile)
    {
        std::string parentDirectory = FileHelper::getParentDirectory(dbfile);
        FileHelper::createDirectory(parentDirectory);
        _helper.open();
        createTable();
    }
    void createTable()
    {
        std::string createTableSql = "create table if not exists binding_table(exchange_name varchar(32), msgqueue_name varchar(32), binding_key varchar(128));";
        assert(_helper.exec(createTableSql));
    }
    void removeTable()
    {
        std::string removeTableSql = "drop table if exists binding_table;";
        _helper.exec(removeTableSql);
    }
    bool insert(Binding::Ptr ptr)
    {
        char insertSql[128] = {0};
        sprintf(insertSql, "insert into binding_table values('%s', '%s', '%s');", ptr->exchangeName.c_str(), ptr->msgQueueName.c_str(), ptr->bindingKey.c_str());
        return _helper.exec(insertSql);
    }
    void remove(const std::string& exchangeName, const std::string& msgQueueName)
    {
        char deleteSql[128] = {0};
        sprintf(deleteSql, "delete from binding_table where exchange_name='%s' and msgqueue_name='%s';", exchangeName.c_str(), msgQueueName.c_str());
        _helper.exec(deleteSql);
    }
    void removeExchangeBindings(const std::string& exchangeName)
    {
        char deleteExchangeSql[128] = {0};
        sprintf(deleteExchangeSql, "delete from binding_table where exchange_name='%s';", exchangeName.c_str());
        _helper.exec(deleteExchangeSql);
    }
    void removeMsgQueueBindings(const std::string& msgQueueName)
    {
        char deleteMsgQueueSql[128] = {0};
        sprintf(deleteMsgQueueSql, "delete from binding_table where msgqueue_name='%s';", msgQueueName.c_str());
        _helper.exec(deleteMsgQueueSql);
    }
    ExchangeBindingMap recovery()
    {
        std::string selectSql = "select * from binding_table;";
        ExchangeBindingMap result;
        _helper.exec(selectSql, selectCallback, &result);
        return result;
    }
private:
    static int selectCallback(void* args, int colNum, char** colValues, char** colNames)
    {
        ExchangeBindingMap* pResult = static_cast<ExchangeBindingMap*>(args);
        Binding::Ptr bindingPtr = std::make_shared<Binding>(colValues[0], colValues[1], colValues[2]);
        // MsgQueueBindingMap msgQueueBingdingMap{{colValues[1], bindingPtr}};
        // pResult->insert({colValues[0], msgQueueBingdingMap});       //这样不行，这样每次都覆盖掉了
        MsgQueueBindingMap& msgQueueBingdingMap = (*pResult)[colValues[0]]; //没有colValues[0]这个key会创建，也不用判断一一次了
        msgQueueBingdingMap.insert({colValues[1], bindingPtr});  //colValues[0]下的  队列和binding_key
        return 0;
    }
private:
    SqliteHelper _helper;
};

class BindingManager
{
public:
    using Ptr = std::shared_ptr<BindingManager>;
    BindingManager(const std::string& dbfile)
        :_mapper(dbfile)
    {
        _manager = _mapper.recovery();
    }
    bool bind(const std::string& exchangeName, const std::string& msgQueueName, const std::string& key, bool durable = true)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _manager.find(exchangeName);          
        if(it != _manager.end() && it->second.find(msgQueueName) != it->second.end()) return false;
        
        Binding::Ptr pBinding = std::make_shared<Binding>(exchangeName, msgQueueName, key);
        MsgQueueBindingMap& msgQueueBindingMap = _manager[exchangeName];
        msgQueueBindingMap.insert({msgQueueName, pBinding});

        if(durable) _mapper.insert(pBinding);
        return true;
    }
    void unBind(const std::string& exchangeName, const std::string& msgQueueName)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _manager.find(exchangeName);
        if(it == _manager.end() || it != _manager.end() && it->second.find(msgQueueName) == it->second.end()) return;
        it->second.erase(msgQueueName);

        _mapper.remove(exchangeName, msgQueueName);
    }
    void removeExchangeBinding(const std::string& exchangeName)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _manager.find(exchangeName);
        if(it == _manager.end()) return;
        // (*it)erase(exchangeName); it是映射本身，
        _manager.erase(exchangeName);
        
        _mapper.removeExchangeBindings(exchangeName);
    }
    void removeMsgQueueBinding(const std::string& msgQueueName)
    {
        std::unique_lock<std::mutex> lock(_mutex);

        for(auto& exchangeBinding : _manager)
            exchangeBinding.second.erase(msgQueueName);

        _mapper.removeMsgQueueBindings(msgQueueName);
    }
    MsgQueueBindingMap getExchangeBindings(const std::string& exchangeName)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _manager.find(exchangeName);
        if(it == _manager.end()) return MsgQueueBindingMap();
        return it->second;
    }
    Binding::Ptr getBinding(const std::string& exchangeName, const std::string& msgQueueName)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _manager.find(exchangeName);
        if(it == _manager.end() || it != _manager.end() && it->second.find(msgQueueName) == it->second.end()) return Binding::Ptr();
        return it->second[msgQueueName];
    }
    bool exists(const std::string& exchangeName, const std::string& msgQueueName)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _manager.find(exchangeName);
        if(it == _manager.end() || it != _manager.end() && it->second.find(msgQueueName) == it->second.end()) return false;
        else return true;
    }
    size_t size()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        size_t ret = 0;
        for(auto& exchangebinding : _manager)
            ret += exchangebinding.second.size();
        return ret;
    }
    void clear()            //就是全部删完，管你是不是durable
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _mapper.removeTable();
        _manager.clear();
    }
private:
    std::mutex _mutex;
    BindingMapper _mapper;
    ExchangeBindingMap _manager;
};